package com.pai4j.log.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.pai4j.common.helper.SessionHelper;
import com.pai4j.common.util.RemoteIpUtil;
import com.pai4j.log.annotation.OperationLog;
import com.pai4j.log.constants.LogConstants;
import com.pai4j.log.entity.OperationLogEntity;
import com.pai4j.log.enums.LogStatusEnum;
import com.pai4j.log.service.ILogService;
import com.pai4j.remote.user.UserServiceClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 日志切面类
 * 通过AOP拦截@OperationLog注解标记的方法，自动记录操作日志
 *
 * @author: CYM-pai
 * @date: 2025/07/29 16:00
 **/
@Slf4j
@Aspect
@Component
public class LogAspect {

    @Resource
    private ILogService logService;
    
    @Resource
    private UserServiceClient userServiceClient;

    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 定义切点：所有标记了@OperationLog注解的方法
     */
    @Pointcut("@annotation(com.pai4j.log.annotation.OperationLog)")
    public void operationLogPointcut() {
    }

    /**
     * 环绕通知：记录操作日志
     */
    @Around("operationLogPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        // 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        
        // 构建日志实体
        OperationLogEntity logEntity = buildLogEntity(joinPoint, operationLog);
        
        Object result = null;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
            
            // 记录成功日志
            logEntity.setStatus(LogStatusEnum.SUCCESS.getCode());
            
            // 记录返回结果
            if (operationLog.recordResult() && result != null) {
                logEntity.setResult(toJsonString(result));
            }
            
        } catch (Exception e) {
            // 记录失败日志
            logEntity.setStatus(LogStatusEnum.FAILURE.getCode());
            logEntity.setErrorMsg(e.getMessage());
            throw e;
        } finally {
            // 计算耗时
            long costTime = System.currentTimeMillis() - startTime;
            logEntity.setCostTime(costTime);
            
            // 保存日志
            if (operationLog.async()) {
                logService.saveOperationLogAsync(logEntity);
            } else {
                logService.saveOperationLog(logEntity);
            }
        }
        
        return result;
    }

    /**
     * 异常通知：记录异常日志
     */
    @AfterThrowing(pointcut = "operationLogPointcut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            OperationLog operationLog = method.getAnnotation(OperationLog.class);
            
            OperationLogEntity logEntity = buildLogEntity(joinPoint, operationLog);
            logEntity.setStatus(LogStatusEnum.FAILURE.getCode());
            logEntity.setErrorMsg(e.getMessage());
            
            if (operationLog.async()) {
                logService.saveOperationLogAsync(logEntity);
            } else {
                logService.saveOperationLog(logEntity);
            }
        } catch (Exception ex) {
            log.error("记录异常日志失败", ex);
        }
    }

    /**
     * 构建日志实体
     */
    private OperationLogEntity buildLogEntity(JoinPoint joinPoint, OperationLog operationLog) {
        OperationLogEntity logEntity = new OperationLogEntity();
        
        // 设置链路追踪ID
        String traceId = MDC.get(LogConstants.TRACE_ID_MDC_KEY);
        if (StringUtils.isEmpty(traceId)) {
            traceId = UUID.randomUUID().toString().replace("-", "");
            MDC.put(LogConstants.TRACE_ID_MDC_KEY, traceId);
        }
        logEntity.setTraceId(traceId);
        
        // 设置用户信息
        try {
            String userId = SessionHelper.getCurrentUserId();
            String userName = userServiceClient.getUserBaseInfo(userId).getData().getUserName();
            if (!StringUtils.isBlank(userId)) {
                logEntity.setUserId(userId);
                logEntity.setUserName(userName);
            } else {
                logEntity.setUserId(LogConstants.SYSTEM_USER);
                logEntity.setUserName(LogConstants.SYSTEM_USER);
            }
        } catch (Exception e) {
            logEntity.setUserId(LogConstants.SYSTEM_USER);
            logEntity.setUserName(LogConstants.SYSTEM_USER);
        }
        
        // 设置基本信息
        logEntity.setModule(StringUtils.isEmpty(operationLog.module()) ? 
                LogConstants.UNKNOWN_MODULE : operationLog.module());
        logEntity.setOperation(StringUtils.isEmpty(operationLog.operation()) ? 
                LogConstants.UNKNOWN_OPERATION : operationLog.operation());
        logEntity.setDescription(operationLog.description());
        logEntity.setLogType(operationLog.type().getCode());
        
        // 设置请求信息
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                logEntity.setMethod(request.getMethod());
                logEntity.setUrl(request.getRequestURL().toString());
                logEntity.setIp(RemoteIpUtil.getRemoteIpByRequest(request));
                logEntity.setUserAgent(request.getHeader("User-Agent"));
            }
        } catch (Exception e) {
            log.debug("获取请求信息失败", e);
        }
        
        // 设置请求参数
        if (operationLog.recordParams()) {
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                String params = buildParams(args, operationLog.ignoreParams());
                logEntity.setParams(params);
            }
        }
        
        logEntity.setCreateTime(new Date());
        
        return logEntity;
    }

    /**
     * 构建请求参数字符串（过滤敏感信息）
     */
    private String buildParams(Object[] args, String[] ignoreParams) {
        try {
            if (args == null || args.length == 0) {
                return null;
            }
            
            List<Object> paramList = new ArrayList<>();
            Set<String> ignoreSet = new HashSet<>(Arrays.asList(ignoreParams));
            
            for (Object arg : args) {
                if (arg == null) {
                    paramList.add(null);
                    continue;
                }
                
                // 如果是基本类型或字符串，直接添加
                if (isPrimitiveOrWrapper(arg.getClass()) || arg instanceof String) {
                    paramList.add(arg);
                } else {
                    // 复杂对象需要过滤敏感字段
                    paramList.add(filterSensitiveFields(arg, ignoreSet));
                }
            }
            
            return toJsonString(paramList);
        } catch (Exception e) {
            log.debug("构建请求参数失败", e);
            return "参数解析失败";
        }
    }

    /**
     * 过滤敏感字段
     */
    private Object filterSensitiveFields(Object obj, Set<String> ignoreFields) {
        try {
            String jsonStr = toJsonString(obj);
            Map<String, Object> map = objectMapper.readValue(jsonStr, Map.class);
            
            // 移除敏感字段
            for (String field : ignoreFields) {
                removeFieldRecursively(map, field);
            }
            
            return map;
        } catch (Exception e) {
            return obj.toString();
        }
    }

    /**
     * 递归移除敏感字段
     */
    @SuppressWarnings("unchecked")
    private void removeFieldRecursively(Map<String, Object> map, String fieldName) {
        if (map == null) {
            return;
        }
        
        map.remove(fieldName);
        
        for (Object value : map.values()) {
            if (value instanceof Map) {
                removeFieldRecursively((Map<String, Object>) value, fieldName);
            } else if (value instanceof List) {
                List<?> list = (List<?>) value;
                for (Object item : list) {
                    if (item instanceof Map) {
                        removeFieldRecursively((Map<String, Object>) item, fieldName);
                    }
                }
            }
        }
    }

    /**
     * 判断是否为基本类型或包装类型
     */
    private boolean isPrimitiveOrWrapper(Class<?> clazz) {
        return clazz.isPrimitive() || 
               clazz == Boolean.class || clazz == Character.class ||
               clazz == Byte.class || clazz == Short.class ||
               clazz == Integer.class || clazz == Long.class ||
               clazz == Float.class || clazz == Double.class;
    }

    /**
     * 对象转JSON字符串
     */
    private String toJsonString(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            return obj != null ? obj.toString() : null;
        }
    }
}
